<template>
	<span class="nex-animated-number">{{ displayText }}</span>
</template>

<script setup lang="ts">
interface Props {
	value: number
	duration?: number
	decimals?: number
	separator?: string
	prefix?: string
	suffix?: string
}

const props = withDefaults(defineProps<Props>(), {
	value: 0,
	duration: 1000,
	decimals: 0,
	separator: ',',
	prefix: '',
	suffix: '',
})

const currentValue = ref(0)
const displayText = ref('')

// 格式化数字
const formatNumber = (num: number): string => {
	const fixed = num.toFixed(props.decimals)
	const [integer, decimal] = fixed.split('.')
	const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, props.separator)
	const result = decimal ? `${formattedInteger}.${decimal}` : formattedInteger
	return props.prefix + result + props.suffix
}

// 缓动函数
const easeOutCubic = (t: number): number => {
	return 1 - Math.pow(1 - t, 3)
}

// 动画更新
const animateTo = (target: number) => {
	const start = currentValue.value
	const change = target - start
	const startTime = Date.now()

	const animate = () => {
		const elapsed = Date.now() - startTime
		const progress = Math.min(elapsed / props.duration, 1)
		const eased = easeOutCubic(progress)
		
		currentValue.value = start + change * eased
		displayText.value = formatNumber(currentValue.value)

		if (progress < 1) {
			requestAnimationFrame(animate)
		} else {
			currentValue.value = target
			displayText.value = formatNumber(target)
		}
	}

	requestAnimationFrame(animate)
}

// 初始化
onMounted(() => {
	currentValue.value = props.value
	displayText.value = formatNumber(props.value)
})

// 监听值变化
watch(
	() => props.value,
	(newValue, oldValue) => {
		if (oldValue !== undefined && newValue !== oldValue) {
			animateTo(newValue)
		}
	}
)
</script>

<style scoped lang="scss">
.nex-animated-number {
	display: inline-block;
	font-variant-numeric: tabular-nums;
	font-weight: inherit;
	color: inherit;
	font-size: inherit;
}
</style>
